///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __Entity_H__
#define __Entity_H__

#include "OgrePrerequisites.h"
#include "OgreCommon.h"

#include "OgreString.h"
#include "OgreMovableObject.h"
#include "math/quaternion.h"
#include "math/vector3.h"
#include "OgreHardwareBufferManager.h"
#include "OgreMesh.h"
#include "OgreRenderable.h"
#include "OgreResourceGroupManager.h"

namespace Ogre {
	
	
	
	class _OgreExport Entity: public MovableObject, public Resource::Listener
	{
		// Allow EntityFactory full access
		friend class EntityFactory;
		friend class SubEntity;
	public:
		
		typedef set<Entity*>::type EntitySet;
		typedef map<unsigned short, bool>::type SchemeHardwareAnimMap;

	protected:

		
		Entity();
		
		Entity( const String& name, const MeshPtr& mesh);

		
		MeshPtr mMesh;

		
		typedef vector<SubEntity*>::type SubEntityList;
		SubEntityList mSubEntityList;


		/// State of animation for animable meshes
		AnimationStateSet* mAnimationState;


		/// Temp buffer details for software skeletal anim of shared geometry
		TempBlendedBufferInfo mTempSkelAnimInfo;
		/// Vertex data details for software skeletal anim of shared geometry
		VertexData* mSkelAnimVertexData;
		/// Temp buffer details for software vertex anim of shared geometry
		TempBlendedBufferInfo mTempVertexAnimInfo;
		/// Vertex data details for software vertex anim of shared geometry
		VertexData* mSoftwareVertexAnimVertexData;
		/// Vertex data details for hardware vertex anim of shared geometry
		/// - separate since we need to s/w anim for shadows whilst still altering
		///   the vertex data for hardware morphing (pos2 binding)
		VertexData* mHardwareVertexAnimVertexData;
		/// Have we applied any vertex animation to shared geometry?
		bool mVertexAnimationAppliedThisFrame;
        /// Have the temp buffers already had their geometry prepared for use in rendering shadow volumes?
        bool mPreparedForShadowVolumes;

		
		const VertexData* findBlendedVertexData(const VertexData* orig);
		
		SubEntity* findSubEntityForVertexData(const VertexData* orig);

		
		void extractTempBufferInfo(VertexData* sourceData, TempBlendedBufferInfo* info);
		
		VertexData* cloneVertexDataRemoveBlendInfo(const VertexData* source);
		
		void prepareTempBlendBuffers(void);
		
		void markBuffersUnusedForAnimation(void);
		
		void restoreBuffersForUnusedAnimation(bool hardwareAnimation);

		
		void bindMissingHardwarePoseBuffers(const VertexData* srcData, 
			VertexData* destData);
			
		
		void initialisePoseVertexData(const VertexData* srcData, VertexData* destData, 
			bool animateNormals);

		
		void finalisePoseNormals(const VertexData* srcData, VertexData* destData);

		/// Cached bone matrices, including any world transform
        Matrix4 *mBoneWorldMatrices;
        /// Cached bone matrices in skeleton local space, might shares with other entity instances.
		Matrix4 *mBoneMatrices;
		unsigned short mNumBoneMatrices;
		/// Records the last frame in which animation was updated
		unsigned long mFrameAnimationLastUpdated;

		/// Perform all the updates required for an animated entity
		void updateAnimation(void);

		/// Records the last frame in which the bones was updated
		/// It's a pointer because it can be shared between different entities with
		/// a shared skeleton.
		unsigned long *mFrameBonesLastUpdated;

		/**
		* A set of all the entities which shares a single SkeletonInstance.
		* This is only created if the entity is in fact sharing it's SkeletonInstance with
		* other Entities.
		*/
		EntitySet* mSharedSkeletonEntities;

		
		bool cacheBoneMatrices(void);

		/// Flag determines whether or not to display skeleton
		bool mDisplaySkeleton;
		
		SchemeHardwareAnimMap mSchemeHardwareAnim;

		/// Current state of the hardware animation as represented by the entities parameters
		bool mCurrentHWAnimationState;

		/// Number of hardware poses supported by materials
		ushort mHardwarePoseCount;
		/// Flag indicating whether we have a vertex program in use on any of our subentities
		bool mVertexProgramInUse;
        /// Counter indicating number of requests for software animation.
        int mSoftwareAnimationRequests;
        /// Counter indicating number of requests for software blended normals.
        int mSoftwareAnimationNormalsRequests;
		/// Flag indicating whether to skip automatic updating of the Skeleton's AnimationState
		bool mSkipAnimStateUpdates;
		/// Flag indicating whether to update the main entity skeleton even when an LOD is displayed
		bool mAlwaysUpdateMainSkeleton;


		/// The LOD number of the mesh to use, calculated by _notifyCurrentCamera
		ushort mMeshLodIndex;

		/// LOD bias factor, transformed for optimisation when calculating adjusted lod value
		Real mMeshLodFactorTransformed;
		/// Index of minimum detail LOD (NB higher index is lower detail)
		ushort mMinMeshLodIndex;
		/// Index of maximum detail LOD (NB lower index is higher detail)
		ushort mMaxMeshLodIndex;

        /// LOD bias factor, not transformed
        Real mMaterialLodFactor;
		/// LOD bias factor, transformed for optimisation when calculating adjusted lod value
		Real mMaterialLodFactorTransformed;
		/// Index of minimum detail LOD (NB higher index is lower detail)
		ushort mMinMaterialLodIndex;
		/// Index of maximum detail LOD (NB lower index is higher detail)
		ushort mMaxMaterialLodIndex;

		
		typedef vector<Entity*>::type LODEntityList;
		LODEntityList mLodEntityList;

		
		SkeletonInstance* mSkeletonInstance;

		/// Has this entity been initialised yet?
		bool mInitialised;

		/// Last parent xform
		Matrix4 mLastParentXform;

		/// Mesh state count, used to detect differences
		size_t mMeshStateCount;

		
		void buildSubEntityList(MeshPtr& mesh, SubEntityList* sublist);

		/// internal implementation of attaching a 'child' object to this entity and assign the parent node to the child entity
		void attachObjectImpl(MovableObject *pMovable, TagPoint *pAttachingPoint);

		/// internal implementation of detaching a 'child' object of this entity and clear the parent node of the child entity
		void detachObjectImpl(MovableObject* pObject);

		/// internal implementation of detaching all 'child' objects of this entity
		void detachAllObjectsImpl(void);

		/// ensures reevaluation of the vertex processing usage
		void reevaluateVertexProcessing(void);

		
		bool calcVertexProcessing(void);
	
		/// Apply vertex animation
		void applyVertexAnimation(bool hardwareAnimation, bool stencilShadows);
		/// Initialise the hardware animation elements for given vertex data
		ushort initHardwareAnimationElements(VertexData* vdata,
			ushort numberOfElements, bool animateNormals);
		/// Are software vertex animation temp buffers bound?
		bool tempVertexAnimBuffersBound(void) const;
        /// Are software skeleton animation temp buffers bound?
        bool tempSkelAnimBuffersBound(bool requestNormals) const;

	public:
		/// Contains the child objects (attached to bones) indexed by name
		typedef map<String, MovableObject*>::type ChildObjectList;
	protected:
		ChildObjectList mChildObjectList;


		/// Bounding box that 'contains' all the mesh of each child entity
		mutable AxisAlignedBox mFullBoundingBox;

		ShadowRenderableList mShadowRenderables;

		
		class _OgreExport EntityShadowRenderable : public ShadowRenderable
		{
		protected:
			Entity* mParent;
			/// Shared link to position buffer
			HardwareVertexBufferSharedPtr mPositionBuffer;
			/// Shared link to w-coord buffer (optional)
			HardwareVertexBufferSharedPtr mWBuffer;
			/// Link to current vertex data used to bind (maybe changes)
			const VertexData* mCurrentVertexData;
			/// Original position buffer source binding
			unsigned short mOriginalPosBufferBinding;
			/// Link to SubEntity, only present if SubEntity has it's own geometry
			SubEntity* mSubEntity;


		public:
			EntityShadowRenderable(Entity* parent,
				HardwareIndexBufferSharedPtr* indexBuffer, const VertexData* vertexData,
				bool createSeparateLightCap, SubEntity* subent, bool isLightCap = false);
			~EntityShadowRenderable();
			
			/// Create the separate light cap if it doesn't already exists
			void _createSeparateLightCap();
			/// Overridden from ShadowRenderable
			void getWorldTransforms(Matrix4* xform) const;
			HardwareVertexBufferSharedPtr getPositionBuffer(void) { return mPositionBuffer; }
			HardwareVertexBufferSharedPtr getWBuffer(void) { return mWBuffer; }
			/// Rebind the source positions (for temp buffer users)
			void rebindPositionBuffer(const VertexData* vertexData, bool force);
			/// Overridden from ShadowRenderable
			bool isVisible(void) const;
			/// Overridden from ShadowRenderable
			virtual void rebindIndexBuffer(const HardwareIndexBufferSharedPtr& indexBuffer);
		};
	public:
		
		~Entity();

		
		const MeshPtr& getMesh(void) const;

		
		SubEntity* getSubEntity(unsigned int index) const;

		
		SubEntity* getSubEntity( const String& name ) const;

		
		unsigned int getNumSubEntities(void) const;

		
		Entity* clone( const String& newName ) const;

		
		void setMaterialName( const String& name, const String& groupName = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME );

		
		
		void setMaterial(const MaterialPtr& material);

		
		void _notifyCurrentCamera(Camera* cam);

		/// Overridden - see MovableObject.
		void setRenderQueueGroup(uint8 queueID);

		/// Overridden - see MovableObject.
		void setRenderQueueGroupAndPriority(uint8 queueID, ushort priority);

		
		const AxisAlignedBox& getBoundingBox(void) const;

		/// merge all the child object Bounds a return it
		AxisAlignedBox getChildObjectsBoundingBox(void) const;

		
		void _updateRenderQueue(RenderQueue* queue);

		
		const String& getMovableType(void) const;

		
		AnimationState* getAnimationState(const String& name) const;
        
        bool hasAnimationState(const String& name) const;
		
		AnimationStateSet* getAllAnimationStates(void) const;

		
		void setDisplaySkeleton(bool display);

		
		bool getDisplaySkeleton(void) const;


        
        Entity* getManualLodLevel(size_t index) const;

        
        size_t getNumManualLodLevels(void) const;

		
		ushort getCurrentLodIndex() { return mMeshLodIndex; }

		
		void setMeshLodBias(Real factor, ushort maxDetailIndex = 0, ushort minDetailIndex = 99);

		
		void setMaterialLodBias(Real factor, ushort maxDetailIndex = 0, ushort minDetailIndex = 99);

		
		void setPolygonModeOverrideable(bool PolygonModeOverrideable);
		
		TagPoint* attachObjectToBone(const String &boneName,
			MovableObject *pMovable,
			const Quaternion &offsetOrientation = Quaternion::IDENTITY,
			const Vector3 &offsetPosition = Vector3::ZERO);

		
		MovableObject* detachObjectFromBone(const String &movableName);

		
		void detachObjectFromBone(MovableObject* obj);

		/// Detach all MovableObjects previously attached using attachObjectToBone
		void detachAllObjectsFromBone(void);

		typedef MapIterator<ChildObjectList> ChildObjectListIterator;
		
		ChildObjectListIterator getAttachedObjectIterator(void);
		
		Real getBoundingRadius(void) const;

		
		const AxisAlignedBox& getWorldBoundingBox(bool derive = false) const;
		
		const Sphere& getWorldBoundingSphere(bool derive = false) const;

        
        EdgeData* getEdgeList(void);
		
		bool hasEdgeList(void);
        
        ShadowRenderableListIterator getShadowVolumeRenderableIterator(
            ShadowTechnique shadowTechnique, const Light* light,
            HardwareIndexBufferSharedPtr* indexBuffer,
            bool extrudeVertices, Real extrusionDistance, unsigned long flags = 0 );

		
		const Matrix4* _getBoneMatrices(void) const { return mBoneMatrices;}
		
		unsigned short _getNumBoneMatrices(void) const { return mNumBoneMatrices; }
		
		bool hasSkeleton(void) const { return mSkeletonInstance != 0; }
		
		SkeletonInstance* getSkeleton(void) const { return mSkeletonInstance; }
		
		bool isHardwareAnimationEnabled(void);

		
		void _notifyAttached(Node* parent, bool isTagPoint = false);
        
        int getSoftwareAnimationRequests(void) const { return mSoftwareAnimationRequests; }
        
        int getSoftwareAnimationNormalsRequests(void) const { return mSoftwareAnimationNormalsRequests; }
        
        void addSoftwareAnimationRequest(bool normalsAlso);
        
        void removeSoftwareAnimationRequest(bool normalsAlso);

		
		void shareSkeletonInstanceWith(Entity* entity);

		
		bool hasVertexAnimation(void) const;


		
		void stopSharingSkeletonInstance();


		/**
		* Returns whether this entity shares it's SkeltonInstance with other entity instances.
		*/
		inline bool sharesSkeletonInstance() const { return mSharedSkeletonEntities != NULL; }

		/**
		* Returns a pointer to the set of entities which share a SkeletonInstance.
		* If this instance does not share it's SkeletonInstance with other instances NULL will be returned
		*/
		inline const EntitySet* getSkeletonInstanceSharingSet() const { return mSharedSkeletonEntities; }

		
		void refreshAvailableAnimationState(void);

		
		void _updateAnimation(void);

        
        bool _isAnimated(void) const;

        
        bool _isSkeletonAnimated(void) const;

		
		VertexData* _getSkelAnimVertexData(void) const;
		
		VertexData* _getSoftwareVertexAnimVertexData(void) const;
		
		VertexData* _getHardwareVertexAnimVertexData(void) const;
		
		TempBlendedBufferInfo* _getSkelAnimTempBufferInfo(void);
		
		TempBlendedBufferInfo* _getVertexAnimTempBufferInfo(void);
		/// Override to return specific type flag
		uint32 getTypeFlags(void) const;
		/// Retrieve the VertexData which should be used for GPU binding
		VertexData* getVertexDataForBinding(void);

		/// Identify which vertex data we should be sending to the renderer
		enum VertexDataBindChoice
		{
			BIND_ORIGINAL,
			BIND_SOFTWARE_SKELETAL,
			BIND_SOFTWARE_MORPH,
			BIND_HARDWARE_MORPH
		};
		/// Choose which vertex data to bind to the renderer
		VertexDataBindChoice chooseVertexDataForBinding(bool hasVertexAnim);

		
		bool _getBuffersMarkedForAnimation(void) const { return mVertexAnimationAppliedThisFrame; }
		
		void _markBuffersUsedForAnimation(void);

		bool isInitialised(void) const { return mInitialised; }

		void _initialise(bool forceReinitialise = false);
		
		void _deinitialise(void);

		void backgroundLoadingComplete(Resource* res);

		/// @copydoc MovableObject::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, bool debugRenderables = false);

        Real _getMeshLodFactorTransformed() const;
		
		void setSkipAnimationStateUpdate(bool skip) {mSkipAnimStateUpdates = skip;}
		
		bool getSkipAnimationStateUpdate() const {return mSkipAnimStateUpdates;}

		void setAlwaysUpdateMainSkeleton(bool update) {mAlwaysUpdateMainSkeleton = update;}

		bool getAlwaysUpdateMainSkeleton() const {return mAlwaysUpdateMainSkeleton;}

	};

	
	class _OgreExport EntityFactory : public MovableObjectFactory
	{
	protected:
		MovableObject* createInstanceImpl( const String& name, const NameValuePairList* params);
	public:
		EntityFactory() {}
		~EntityFactory() {}

		static String FACTORY_TYPE_NAME;

		const String& getType(void) const;
		void destroyInstance( MovableObject* obj);

	};
	
	

} // namespace

#endif
